package fourth_assignment;
import java.util.LinkedList;
import java.util.Vector;

import first_assignment.DistributionCreator;
import first_assignment.Consts_1st.Movie;
import second_assignment.Consts_2nd;
import second_assignment.Manager;
import second_assignment.Manager.Client;
import third_assignment.Consts_3rd;

public class StaticDynamicSimulator {

	private DynamicManager dynamicManager;
	private second_assignment.Manager staticManager;
	Vector<Integer> newMoviesPerDay;
	
	/**
	 * Initialize F and latest requests vectors
	 */
	public void StaticDynamicInit(){
		Vector<Integer> moviesAmount = new Vector<Integer>(3);
	    Vector<Double> probability = new Vector<Double>(3);
	    Vector<Integer> popularityDistrib = new Vector<Integer>(3);
	    
	    moviesAmount.add(0, Consts_2nd.M_Library);
	    moviesAmount.add(1, Consts_2nd.M_Recorded);
	    moviesAmount.add(2, Consts_2nd.M_RecentlyBroadcast);

	    probability.add(0, Consts_2nd.p1);
	    probability.add(1, Consts_2nd.p2);
	    probability.add(2, Consts_2nd.p3);
	
	    popularityDistrib.add(0, Consts_2nd.a1);
	    popularityDistrib.add(1, Consts_2nd.a2);
	    popularityDistrib.add(2, Consts_2nd.a3);
	    staticManager = new Manager(probability, moviesAmount, popularityDistrib);
	
	    newMoviesPerDay = new Vector<Integer>(3);
	    newMoviesPerDay.add(0, (int) java.lang.Math.ceil(Consts_3rd.Library_NewMovieFactor * Consts_2nd.M_Library));
	    newMoviesPerDay.add(1, (int) java.lang.Math.ceil(Consts_3rd.Recorded_NewMovieFactor * Consts_2nd.M_Recorded));
	    newMoviesPerDay.add(2, (int) java.lang.Math.ceil(Consts_3rd.Recently_NewMovieFactor * Consts_2nd.M_RecentlyBroadcast));

	    dynamicManager = new DynamicManager(staticManager.getRecordedArray(), staticManager.getRecentArray(), staticManager.getLibraryArray());
	}
	

    /**
     * simulation of 24 hours
     */
    public int[] ClientsManagment24(LinkedList<Request> requestsList) {
    	Vector<LinkedList<Client>> Departure = new Vector<LinkedList<Client>>();
    	int[] retVals = new int[3]; 
        int clientsCounter = 0;
        int rejectedClients = 0;
        int servedFromRootClients = 0;
        int nextArrivalTime = 0;
        Request request = null;
        int[] requested = new int[3];
        int[] rejected = new int[3];
        for(int i = 0; i < Consts_2nd.SecondsPerDay; ++i) {
            if ((i % 3600) == 0) { System.out.print("-*"); }
        	if (i >= Consts_2nd.ClientStayInSeconds) {
                if (Departure.size() > 0) {
            		LinkedList<Client> deprtr = Departure.get(0);
            		while (deprtr.size() > 0 && deprtr.getFirst() != null) {
            			LeaveClient(i, deprtr.getFirst());
                    	deprtr.removeFirst();
            		}
                	Departure.remove(0);
                }
        	}

        	if (requestsList.size() > 0){
	        	request = (Request)requestsList.getFirst();
	        	nextArrivalTime = request.time;
        	}
        	if (null == request) break;

        	LinkedList<Client> deprtr = new LinkedList<Client>();
            while ((i == nextArrivalTime) && (i < Consts_2nd.ArrivalsOkInSeconds)) {

                if (requestsList.size() > 1){
                    requestsList.removeFirst();
                    request = requestsList.getFirst();
                    nextArrivalTime = request.time;
                }
                else { 
                    requestsList.clear();
                    break;
                }
            	Client newClient = null;
                // add new client
                Movie movie = null;
                switch (request.type) {
                case 0:
                    movie = staticManager.getLibraryArray().get(request.index);
                    break;
                case 1:
                    movie = staticManager.getRecordedArray().get(request.index);
                    break;
                case 2:
                    movie = staticManager.getRecentArray().get(request.index);
                    break;
                }
                int region = request.region;
                clientsCounter++;
                requested[request.type]++;
                newClient = staticManager.OccupyMovie(movie.movieName, region, clientsCounter);

                if (newClient != null && region == newClient.getRegion()){
                	AcceptClient(i, newClient, movie);
                }
                else
                {
                    boolean served = (newClient != null);
            	    RejectClient(i, region, clientsCounter, movie, served);
                    if (!served) {
                        rejectedClients++;
                        rejected[request.type]++;
                        continue;
                    }
                    else { servedFromRootClients++; }
                }
                deprtr.addLast(newClient);
            }

            if (i >= Consts_2nd.ClientStayInSeconds) {
            	Departure.add(Consts_2nd.ClientStayInSeconds - 1, deprtr);
            } else {
            	Departure.add(i, deprtr);
            }
        }
        System.out.println();
        System.out.println("Number of Clients: "+clientsCounter+" of which "+rejectedClients+" rejected, and " + servedFromRootClients + " serviced from root.");
        System.out.println("Requests: (Library-" + requested[0]+ ", Recorded-"+requested[1]+", Recently_Broadcast-"+ requested[2]+")");
        System.out.println("Rejects: (Library-" + rejected[0]+ ", Recorded-"+rejected[1]+", Recently_Broadcast-"+ rejected[2]+")");
        System.out.println("Rejected clients percentage is: " + ((double)rejectedClients/((double)clientsCounter))*100);
        System.out.println("Serviced clients percentage is: " + ((1.0 - (double)rejectedClients/((double)clientsCounter))*100));
        //System.out.println("* Note that the cost equals to the number of clients served from the root cluster (1 Removal + 1 Addition)");
        
        retVals[0] = clientsCounter;
        retVals[1] = rejectedClients;
        retVals[2] = servedFromRootClients;
        return retVals;
    }
    
    public void AcceptClient(int time, Client newClient, Movie movie){
//    	System.out.println("Time = " + time 
//    			+", Client " + newClient.getClientId() 
//    			+ " from R" + (newClient.getRegion()+1) 
//    			+ " arrives, requested file: " 
//    			+ newClient.getMovieName() + " serviced form disk: " 
//    			+ newClient.getDiskNumber() 
//    			+ "(in C" + newClient.getClusterId() + ")");

    	dynamicManager.AcceptClient(newClient, movie);
    }

    public void RejectClient(int time, int region, int clientsCounter, Movie movie, boolean served){
//    	if (served) {
//    	    System.out.println("Time = " + time 
//                    +", Client " + clientsCounter
//                    + " from R" + (region + 1)
//                    + " arrives, requested file: " 
//                    + movie.movieName + " , Failure - served from root");
//    	}
//    	else {
//            System.out.println("Time = " + time 
//        			+", Client " + clientsCounter
//        			+ " from R" + (region + 1)
//        			+ " arrives, requested file: " 
//        			+ movie.movieName + " , rejected");
//    	}
    	boolean addMovieCopy = dynamicManager.RejectClient(region, movie, served);
    	
    	for (int k=region,j=0; j<3; ++j,k=(k+1)%3){
        	// TODO: change the flow to check if the given movie_to_replace is undeletable
        	if (addMovieCopy && (Consts_4th.MAX_FAILURES <= movie.rejectCount[k])){
        	    int[] deletedCopyLocation = new int[2];
        	    Movie movieToDel = dynamicManager.RemoveLeastRequestedMovie(k);//, movie.movieType);
        	    deletedCopyLocation = DeleteCopyFromRegion(k, movieToDel.movieName);
        	    while (deletedCopyLocation[1] == -1) {
        	        movieToDel = dynamicManager.getNextLeastRequestedMovie(k);//, movie.movieType);
        	        if (null == movieToDel) {
        	            System.out.println("ERROR: unable to add movie '" + movie.movieName +
        	                    "', no space available in region: " + k);
        	            return;
        	        }
        	        deletedCopyLocation = DeleteCopyFromRegion(k, movieToDel.movieName);
        	    }
        	    movieToDel.copies--;
        	    if (!staticManager.AddCopy(deletedCopyLocation[0], deletedCopyLocation[1], movie)) {
        	        int copies = movie.copies;
        	        movie.copies = 2;
        	        if (!staticManager.AddNewMovie(movie)) {
        	            System.out.println("ERROR: unable to add movie '" + movie.movieName + "'.");
        	        }
        	        movie.copies = copies;
        	    }
        	    movie.copies++;
        	}
    	}
    }
    
    private int[] DeleteCopyFromRegion(int region, String movieCopyToDel) {
		return staticManager.RemoveMovieFromRegion(region, movieCopyToDel);
	}
    public void LeaveClient(int time, Client clientLeaves){
    	staticManager.ReleaseCopy(clientLeaves.getClusterId(), clientLeaves.getDiskNumber(), clientLeaves.getMovieName());
//    	System.out.println("Time = " + time 
//    			+", Client " + clientLeaves.getClientId() +" leaves");
    }

    /**
     * adds new programs and removes lowest probability ones.
     * implementation of the initial static assignment.
     * @param day - the number of the sequential day in the simulation.
     */
    public int NewDay(int day) {
        int cost = 0;
        // Create a new movies list (to add to the system).
        Vector<Movie> newRecorded = staticManager.CreateNewSetOfMovies(newMoviesPerDay.get(1), "Recorded_Day-" + (day + 1), 1);
        Vector<Movie> newRecent = staticManager.CreateNewSetOfMovies(newMoviesPerDay.get(2), "RecentlyBroadcast_Day-" + (day + 1), 2);

        DistributionCreator recentDc = staticManager.getRecentlyBroadcastDistributionClass();
        DistributionCreator recordDc = staticManager.getRecordedDistributionClass();
        Vector<String> toDelete_Recorded = dynamicManager.getMoviesToDelete(newMoviesPerDay.get(1), 1, 
                recordDc.getPopularityClassSize(recordDc.getPopularityClassNumber() - 1));
        Vector<String> toDelete_Recent = dynamicManager.getMoviesToDelete(newMoviesPerDay.get(2), 2,
                recentDc.getPopularityClassSize(recentDc.getPopularityClassNumber() - 1));

        //staticManager.PrintStats("BDay" + day);
        for (int i = 0; null != toDelete_Recorded && i < toDelete_Recorded.size(); ++i){
            newRecorded.get(i).copies = staticManager.DeleteAllMovieCopies(toDelete_Recorded.get(i))+1;
            if (!staticManager.AddNewMovie(newRecorded.get(i))) {
                //System.out.println("ERROR 1");
            }
            newRecorded.get(i).copies--;
            cost += newRecorded.get(i).copies * 2;
        }
        //staticManager.PrintStats("MDay" + day);
        for (int i = 0; null != toDelete_Recent && i < toDelete_Recent.size(); ++i){
            newRecent.get(i).copies = staticManager.DeleteAllMovieCopies(toDelete_Recent.get(i))+1;
            if (!staticManager.AddNewMovie(newRecent.get(i))) {
                //System.out.println("ERROR 2");
            }
            newRecent.get(i).copies--;
            cost += newRecent.get(i).copies * 2;
        }

        staticManager.getRecentArray().addAll(newRecent);
        staticManager.getRecordedArray().addAll(newRecorded);

        // Set appropriate vectors to recognize the new movies.
        dynamicManager.SetNewMovies(newRecorded);
        dynamicManager.SetNewMovies(newRecent);

        //staticManager.PrintStats("ADay" + day);
        dynamicManager.ResetRequestCount();
        return cost;
    }
}

